#include "conexionesRAID.h"

/*
 * Retorna la respuesta del handshake!!!!
 */
t_stream *analizarHandshakePFS(char *mensaje, t_socket_client *client){
	t_stream *stream;
	t_peticion *peticion = decodificacionPaquete(mensaje);
	if (peticion->tipoPeticion == 0){
		t_peticion *pet = respuestaHandshakePFS();
		stream = codificarPaquete(pet);
	}
	else{
		logMessage(LOG_ERR,"Peticion distinto de 0, metodo:analizarHandshakePFS(char *mensaje)");
		return stream;
	}
	if (enviarMensaje(client, stream->data) <= 0){
		logMessage(LOG_ERR,"Socket no puedo mandar la respuesta");
	}
	return NULL;
}

/*Retorna la respuesta del handshake!!!*/
t_stream *analizarHandshakePPD(char *mensaje){
	t_stream *stream;
	t_peticion *peticion = decodificacionPaquete(mensaje);
	if (peticion->tipoPeticion == 0){

		int idDisco = atoi(peticion->sPayload->data);
		t_infoDisco *info = clousreBuscarDiscoById(idDisco);
		/*Veo si el disco ya esta en la tabla de discos (PPD)*/
		t_peticion *petRespuesta = respuestaHandshakePDD(info != NULL);
		stream = codificarPaquete(petRespuesta);
	}
	else{
		logMessage(LOG_ERR,"Peticion distinto de 0, metodo:analizarHandshakePPD(char *mensaje)");
		return stream;
	}
	return stream;
}
/**/

/*
 * Abro conexiones para escuchar tanto al PFS y al PPD
 */
void iniciarConexionConDispositivos()
{
	/*Genero socket servers */
	t_socket_server *serverPFS = socket_crearSocketParaPFS();
	t_socket_server *serverPPD = socket_crearSocketParaPPD();

	t_list *listServer = collection_list_create();
	collection_list_add(listServer, serverPFS);
	collection_list_add(listServer, serverPPD);

	logMessage(LOG_INFO,"agrego servers a lista para select *iniciarConexionConDispositivos*");
	sockets_select(listServer, NULL, 0,onAcceptClosure,NULL);

	/*Ver si hay q hacer algo desp de esto*/
}

void *onAcceptClosure(t_socket_server *server){

	if (isPFS(server)){
		generarThreadPFS(server);
	}
	else{
		/*generarThreadPPD(server);*/
	}
}

void analizarMensajePFS(char* mensaje, t_socket_client *client){

	t_stream *stream;
	t_peticion *peticion = decodificacionPaquete(mensaje);

	switch (peticion->tipoPeticion)
	{
	   	 case Lectura:
		   encolarPeticiones(peticion, peticiones);
		   break;
		 case Escritura:
		   encolarPeticiones(peticion, peticiones);
		   break;
		 default:
		   printf("\n  ERROR DE MENSAJE...");
		   break;
	}
}


void analizarMensajePPD(char* mensaje, t_socket_client *client){

	t_stream *stream;
	t_list *tablaDisco;
	t_list *listPetResueltas;
	t_infoDisco* disco;

  /*Falta ver envio de respuesta por socket*/
   t_peticion *peticion = decodificacionPaquete(mensaje);

   switch (peticion->tipoPeticion)
   {
		case Handshake:
			stream = analizarHandshakePPD(mensaje);
			tablaDisco = getListaInfoDisco();
			/*Cuando agrego el disco estoy creando un socket client para el envio de datos*/
			disco = inicializarInfoDisco(atoi(peticion->sPayload->data));/*Tomo id de disco*/
			agregarATablaDisco(tablaDisco,disco,peticion->ip);
			disco->tiempoInicio = time(NULL);

			if (enviarMensaje(client,stream->data) <= 0){
				logMessage(LOG_ERR,"Socket no puedo mandar la respuesta.enviarMensaje(client,stream->data)");
			}

			break;
		case Lectura:
			/*Genero peticion*/
			listPetResueltas = getListaPeticionesResueltas();
			collection_list_add(listPetResueltas, peticion);
			/*Agrego resultadoPPD a cola de salida*/
			break;
		case Escritura:
			/*Genero peticion*/
			listPetResueltas = getListaPeticionesResueltas();
			collection_list_add(listPetResueltas, peticion);
			/*Agrego resultadoPPD a cola de salida*/
			break;
		case LecturaSincro:
			/*es una peticion para sincronizacion*/
			generarPeticionDeEscritura(peticion);
			break;
		case FinSincro:
			disco = clousreBuscarDiscoById(atoi(peticion->sPayload->data));
			validarFinSincro(disco, disco->tiempoInicio);
			break;
		default:
			break;
   }
}

/*
 * Poll de conexiones para poder escuchar al proceso fileSystem
 */
int inicializarPollConexionesConPFS(){
	t_config *config = getConfig();
	t_array *arrayConexiones = array_create(config->tamConexiones);
	int i;
	int error = 0;
	for (i = 0; i < config->tamConexiones;i++){
		t_socket_server* server = sockets_createServer(config->ip, config->puertoPFSPoll + i);
		array_set(arrayConexiones,i,server,(void*)disposeConexion);
		generarThreadSocketPoll(server);
	}
	return error;
}

/*
 * Genero thread y hago el canal de conexion con el socket de raid o ppd
 */
void *iniciarSocketServerPFS(t_socket_server *server){
	t_socket_client*client = sockets_accept(server);
	t_stream *stream;
	if (client != NULL){
		while (1){
			t_list *listPeticionesResueltas = getListaPeticionesResueltas();
			t_socket_buffer *buffer = sockets_recv(client);
			analizarMensajePFS(buffer->data, client);

			/*Tomo de una lista mi paquete a responder*/
			t_peticion *peticionResuelta = getPeticionResuelta(listPeticionesResueltas, client);
			while (peticionResuelta != NULL){
				peticionResuelta = getPeticionResuelta(listPeticionesResueltas, client);

				stream = codificarPaquete(peticionResuelta);

				if (sockets_sendString(client, stream->data) > 0){
					logMessage(LOG_INFO,"Error al enviar al string *iniciarSocketServerPFS*");
				}/*TEngo que loguear */
			}

			stream_destroy(stream);
		}
	}
	else{
		logMessage(LOG_ERR,"Cliente is NULL");
	}
}


/*Fin de poll de conexiones*/

void socket_conectarPPD(t_socket_server *socketServer){
	int cierroSocket = 0;
	t_stream *stream;
	t_socket_buffer *buffer;
	t_socket_client *client = sockets_accept(socketServer);
	if (client != NULL){
		buffer= sockets_recv(client);
		if (buffer != NULL){
			/*Recibo el handshake*/
			/*Si handshake es OK! =>lo agrego a mi tabla de discos y envio respuesta handshake*/
			stream = analizarHandshakePPD(buffer->data);
			if (enviarMensaje(client,stream->data) > 0){

				while (cierroSocket == 0){/*Faltaria condicion*/
					buffer= sockets_recv(client);
					if (buffer != NULL){
						analizarMensajePPD(buffer->data, client);
					}
					else
					{
						 validarCaidaDisco(client);
								logMessage(LOG_ERR,"Buffer is NULL, metodo: socket_conectarPPD ");
					}/*Logueo que hubo error em el recv*/
						cierroSocket = 1;
				}

			}
			stream_destroy(stream);
		}
		sockets_bufferDestroy(buffer);
	}
	sockets_destroyClient(client);
}

void socket_conectarPFS(t_socket_server *socketServer){
	t_socket_client *client = sockets_accept(socketServer);
	t_stream *stream;
	if (client != NULL){
		t_socket_buffer *buffer = sockets_recv(client);
		/*recibo handshake*/
		if (buffer != NULL){
			stream = analizarHandshakePFS(buffer->data, client);
			if (enviarMensaje(client,stream->data) > 0){
				if (inicializarPollConexionesConPFS() > 0){
					logMessage(LOG_INFO,"Se inicializo Poll de conexiones OK");
				}/*Logueo info se inicializo ok!!!*/
			}
			else
			{
				logMessage(LOG_ERR,"No se pudo inicializar Poll de conexiones OK");
				/*Logueo error*/
			}
			stream_destroy(stream);
		}
		sockets_bufferDestroy(buffer);
		sockets_destroyClient(client);
	}
	else
		logMessage(LOG_ERR,"WTF??????? PQ no hace el acetp???")
}

int socket_enviarPeticionPPD(t_socket_client *socketClient, char* data){

	int i = sockets_sendString(socketClient, data);
	if (i < 0){
		printf("ERROR");
			logMessage(LOG_ERR,"No se pudo inicializar Poll de conexiones OK");
		/*logeo error*/
	}
	return i;/*Ver que se retorna cuando arroja un error de conexion de disco*/
}


/*Me devolvio ERROR el socket. Tengo que evaluar si el disco que se desconecto era el disco D*/

void validarCaidaDisco(t_socket_server *server){
	if (collection_list_size(tablaDisco) == 1){
		logMessage(LOG_ERR,"EXPLOTO!!! REVENTO EL DISCO DE DATA");/*Log que se rompio todo y se hace EXIT*
		 * /
		/*CORTAR CONEXIONES!*/
		exit(0);
	}

	int cant = 0;
	t_infoDisco *discoCaido = clousreBuscarDiscoByIp(tablaDisco, sockets_getIp(server->socket));
	if (discoCaido->soyDiscoD){

		int cantSincro = 0;
		while (cant <= collection_list_size(tablaDisco)){
			t_infoDisco *disco = (t_infoDisco*)collection_list_get(tablaDisco,cant);
			if (disco->estado == Sincronizando){
				cantSincro++;
				cant++;
			}
		}

		if (cantSincro == collection_list_size(tablaDisco)-1){
			logMessage(LOG_WARN,"DANGER!!!! Cortar Conexiones!!!.");/*LOG CORTAR CONEXIONES!*/
			exit(0);
		}
		else
		{
			int cant = 0;
			t_queue *colaPendienteDisco = (t_queue*)discoCaido->colaPendientesPetDisco;
			while (cant <= collection_list_size(colaPendienteDisco)){
				t_peticion *peticion = (t_peticion*)collection_list_get(colaPendienteDisco,cant);
				if(peticion->tipoPeticion == Lectura){
					encolarPeticiones(peticion, peticiones);
				}
				cant++;
			}
			int discoSincronizado = 0;
			t_raid pRaid;
			while (discoSincronizado <= collection_list_size(tablaDisco)){
				t_infoDisco *disco = (t_infoDisco*)collection_list_get(tablaDisco,discoSincronizado);
				if (disco->estado == Activo){
					disco->soyDiscoD =1;
					break;
				}else{
					discoSincronizado++;
				}
			}
		}
	}
	else{

		t_queue *colaPendienteDisco = (t_queue*)discoCaido->colaPendientesPetDisco;
		while (cant <= collection_list_size(colaPendienteDisco)){

			t_peticion *peticion = (t_peticion*)collection_list_get(colaPendienteDisco,cant);
			if(peticion->tipoPeticion == Lectura){
				encolarPeticiones(peticion, peticiones);
			}
			cant++;
		}
	}
}



/*Envio de peticiones al PPD*/

/*
 * Cliente PPD
 */
/*Ver q onda*/
/*void inicializarClientePPD(){
	t_config *config = getConfig();
	t_socket_client *client = sockets_createClient();
}*/

/*Fin de envio de peticiones al PPD*/

void *disposeConexion(t_conexion *conexion){
	sockets_destroyServer(conexion->server);
	free(conexion);
}

t_socket_server *socket_crearSocketParaPFS()
{
	t_config *config = getConfig();
	t_socket_server *server = sockets_createServer(config->ip, config->puertoPFS);
	if (server == NULL)
		logMessage(LOG_ERR,"Error al generar el socket server *socket_crearSocketParaPFS*");

	return server;
}

t_socket_server *socket_crearSocketParaPPD()
{
	t_config *config = getConfig();
	t_socket_server *server = sockets_createServer(config->ip, config->puertoPPD);
	if (server == NULL)
		logMessage(LOG_ERR,"error al generar el socket server *socket_crearSocketParaPPD*");

	return server;
}

unsigned char isPFS(t_socket_server *server){
	t_config *config = getConfig();
	unsigned char result = 1;
	if (sockets_getPort(server->socket) == config->puertoPFS)
	{
		result = 0;
	}

	return result;
}

int enviarMensaje(t_socket_client *client, char*msj)
{
	return sockets_sendString(client, msj);
}

t_peticion* respuestaHandshakePFS(){
	t_peticion* paqueteRespuesta = inicializarPeticion();

	/*Para aceptar el handshake, el RAID debe estar activo*/
	if (first == 1) {
		/*acepto handshake*/
		paqueteRespuesta->tipoPeticion = Handshake;
		paqueteRespuesta->length = 0;
		paqueteRespuesta->sPayload->nSector = 0;
		paqueteRespuesta->flagSincro = 0;
	}
	else{
		/*rechazo handshake*/
		paqueteRespuesta->tipoPeticion = Handshake;
		paqueteRespuesta->length = 0;
		paqueteRespuesta->sPayload->nSector = 0;
		memcpy(paqueteRespuesta->sPayload->data,"Error: RAID no activo",strlen("Error: RAID no activo") +1);
		paqueteRespuesta->flagSincro = 0;
	}
	return paqueteRespuesta;
}

t_peticion* respuestaHandshakePDD(int error){
	t_peticion* paqueteRespuesta = inicializarPeticion();
	if (error > 0){
		/*acepto handshake*/
		paqueteRespuesta->tipoPeticion = '0';
		paqueteRespuesta->length = 0;
		paqueteRespuesta->sPayload->nSector = 0;
		paqueteRespuesta->sPayload->data = NULL;
		paqueteRespuesta->flagSincro = 0;
	}
	else{
		paqueteRespuesta->tipoPeticion = Handshake;
		paqueteRespuesta->length = 0;
		paqueteRespuesta->sPayload->nSector = 0;
		memcpy(paqueteRespuesta->sPayload->data,"Error: Id de disco existente.",strlen("Error: Id de disco existente.") +1);
		paqueteRespuesta->flagSincro = 0;
	}
	return paqueteRespuesta;
}
